﻿namespace Paw.Services
{
    using System;
    using System.Web;
    using System.Linq;
    using System.Xml.Linq;
    using System.Collections.Generic;
    using System.Collections.Concurrent;
    using System.Runtime.Serialization;

    using System.ServiceModel;
    using System.ServiceModel.Activation;
    using System.ServiceModel.Web;
    using System.ServiceModel.Channels;

    using System.Net;
    using System.Xml;
    using System.IO;

    
    //[ServiceBehavior(InstanceContextMode= InstanceContextMode.Single,ConcurrencyMode = ConcurrencyMode.Multiple)]
    [ServiceBehavior(IncludeExceptionDetailInFaults = false)]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class NotificationService : INotificationService
    {
        
        // The Notification Service
        public NotificationService()
        {
            _messages = new ConcurrentDictionary<string, ConcurrentQueue<NotificationMessage>>();
        }

        // Notifying
        public void Notify(Message message)
        {
            //Greb the notification from the message body
            var queryParams = ParseQueryString(message.Headers.To.Query);
            var body = message.GetReaderAtBodyContents();

            XElement data = XElement.Load(body);

            var queue = _messages.GetOrAdd(queryParams["id"], Key => new ConcurrentQueue<NotificationMessage>());

            queue.Enqueue(new NotificationMessage
            {
                MessageContentBase64 = (string)data,
            });

            //Log.Default.TraceInformation("Notification from: {0}", queryParams["id"]);
        }

        private class NotificationMessage
        {
            public string MessageContentBase64 { get; set; }
        }

        // Tracking the devices to the channel URI list
        private static Dictionary<Guid, Uri> SubscritionURIs = new Dictionary<Guid, Uri>();
        private ConcurrentDictionary<string, ConcurrentQueue<NotificationMessage>> _messages;
        private static IDictionary<string, string> ParseQueryString(string queryString)
        {
            var queryItems = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);

            queryString = queryString.TrimStart('?');
            string[] strings = queryString.Split('&');

            foreach (string queryItem in strings)
            {
                int equalsIndex = queryItem.IndexOf('=');
                if (equalsIndex >= 0)
                {
                    queryItems.Add(queryItem.Substring(0, equalsIndex),
                                    queryItem.Substring(equalsIndex + 1));
                }
                else
                {
                    queryItems.Add(queryItem, null);
                }
            }

            return queryItems;
        }


        // Subsribe The device 
        public void SubscribeMyPhone(Guid phoneID, string channelURI)
        {
            Uri thisURI = new Uri(channelURI);
            if (SubscritionURIs.ContainsKey(phoneID))
            {
                //updating the existing URI entry for device
                SubscritionURIs[phoneID] = thisURI;
            }
            else
            {
                //add subscription for the device
                SubscritionURIs.Add(phoneID, thisURI);
            }

            // Save in database
        }

        // Notification Types

        // Implementing Raw Notification
        // You can use raw notifications in addition to Tile 
        // and toast notifications to send information directly 
        // to the application.
        public void PushRawData(string raw_message)
        {
            // Encoding the message as byte[]
            System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();

            // Sending the message to subscribers
            sendPushToSubscribedURIs(encoding.GetBytes(raw_message), "raw");

        }

        // Implementing Toast Notification 
        // Important notifications for immediate viewing,
        // such as breaking news. Or in our case a workflow
        // assignation or a task assignation or a redirection
        // or a delay in a task validation
        public void PushToast(string toast_title, string toast_message)
        {

            // Constructing the notification structure and contents.
            // TO DO integrate the name of tasks
            // TO DO integrate a timer to send toast notification regulary

            // Defining settings for XML Writer
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.OmitXmlDeclaration = true;
            settings.NewLineOnAttributes = true;

            // Constructing the structure
            MemoryStream myStream = new MemoryStream();
            XmlWriter myWriter = XmlWriter.Create(myStream);

            myWriter.WriteStartDocument();
            myWriter.WriteStartElement("wp", "Notification", "WPNotification");
            myWriter.WriteStartElement("wp", "Toast", "WPNotification");
            myWriter.WriteStartElement("wp", "Text1", "WPNotification");
            myWriter.WriteValue(toast_title);
            myWriter.WriteEndElement();
            myWriter.WriteStartElement("wp", "Text2", "WPNotification");
            myWriter.WriteValue(toast_message);
            myWriter.WriteEndElement();
            myWriter.WriteEndElement();
            myWriter.WriteEndDocument();
            // YOU can add other Elements

            // Transferring XML from writer buffer to stream
            myWriter.Flush();

            // Send message to subscribers
            sendPushToSubscribedURIs(myStream.ToArray(), "toast");
        }

        // Informational notifications such as a temperature change for a weather application.
        public void PushTileupdate(string tile_title, int tile_count, string TileImageURL)
        {
            // Use XmlWriter to construct notification structure/contents.
            MemoryStream myStream = new MemoryStream();
            XmlWriter myWriter = XmlWriter.Create(myStream);

            myWriter.WriteStartDocument();

            myWriter.WriteStartElement("wp", "Notification", "WPNotification");

            myWriter.WriteStartElement("wp", "Tile", "WPNotification");

            myWriter.WriteStartElement("wp", "BackgroundImage", "WPNotification");
            myWriter.WriteValue(TileImageURL);
            myWriter.WriteEndElement();

            myWriter.WriteStartElement("wp", "Count", "WPNotification");
            myWriter.WriteValue(tile_count);
            myWriter.WriteEndElement();

            myWriter.WriteStartElement("wp", "Title", "WPNotification");
            myWriter.WriteValue(tile_title);
            myWriter.WriteEndElement();

            myWriter.WriteEndElement();

            myWriter.WriteEndDocument();

            //Transfer Xml output from myWriter's buffer to myStream.
            myWriter.Flush();

            //Send this message to all subscribed devices.
            sendPushToSubscribedURIs(myStream.ToArray(), "tile");
        }

        // Allowing the Notification Service to batch notifications
        // will let the service group messages togetehr with notifications 
        // from other apps to save phone battery life.
        public enum BatchingIntervalValues
        {
            ImmediateTile = 1,
            ImmediateToast = 2,
            ImmediateRaw = 3,
        }

        private void sendPushToSubscribedURIs(byte[] p, string notificationType)
        {
            foreach (var uri in SubscritionURIs.Values)
            {
                //Headers for HTTP Post
                var request = (HttpWebRequest)WebRequest.Create(uri); // Creating the channel URI
                request.Method = WebRequestMethods.Http.Post;
                request.ContentType = "text/xml";
                request.ContentLength = p.Length;
                // Unique ID for the message
                request.Headers.Add("X-MessageID", Guid.NewGuid().ToString());
                //throw new NotImplementedException();

                // Configure different types of notification
                switch (notificationType)
                {
                    case "toast":
                        request.Headers["X-WindowsPhone-Target"] = "toast";
                        request.Headers.Add("X-NotificationClass", ((int)BatchingIntervalValues.ImmediateToast).ToString());
                        break;

                    case "tile":
                        request.Headers["X-WindowsPhone-Target"] = "tile";
                        request.Headers.Add("X-NotificationClass", ((int)BatchingIntervalValues.ImmediateTile).ToString());
                        break;

                    case "raw":
                        request.Headers["X-WindowsPhone-Target"] = "raw";
                        request.Headers.Add("X-NotificationClass", ((int)BatchingIntervalValues.ImmediateRaw).ToString());
                        break;
                }

                // Merging hearders with payload
                using (var requestStream = request.GetRequestStream())
                {
                    requestStream.Write(p, 0, p.Length);
                }

                // Send notification to the device
                var response = (HttpWebResponse)request.GetResponse();
            }
        }


        //// Get Notification by ID
        //public Message GetNotifications(string id)
        //{
        //    XElement response = new XElement("notifications");
        //    ConcurrentQueue<NotificationMessage> queue;

        //    //Log.Default.TraceInformation("Checking notifications for: {0}", id);

        //    if (_messages.TryGetValue(id, out queue))
        //    {
        //        NotificationMessage message;
        //        while (queue.TryDequeue(out message))
        //        {
        //            response.Add(new XElement("notification", message.MessageContentBase64));

        //            //Log.Default.TraceInformation("De-queued notification for: {0}", id);
        //        }
        //    }

        //    WebOperationContext.Current.OutgoingResponse.Headers[HttpResponseHeader.CacheControl] = "no-cache";
        //    return WebOperationContext.Current.CreateXmlResponse(response);
        //}

    }

}
